﻿using Dapper;
using Dapper.Common;

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;

namespace DapperExtensions
{
    public class DapperBaseRepository<T> : IRepository<T> where T : class
    {
        private string _connectionName;
        protected IDbConnection GetOpenConnection()
        {
            var connection = DataBaseUtility.GetInstance(_connectionName).GetConnection();
            if (connection.State != ConnectionState.Open)
                connection.Open();

            return connection;
        }

        public DapperBaseRepository(string connectionName = "default")
        {
            _connectionName = connectionName;
        }

        /// <summary>
        /// List<ISort> sortList = new List<ISort>();
        /// sortList.Add(Predicates.Sort<myPoco>(x => x.Name, false));
        /// var result = GetList<myPoco>(null, sortList).ToList();
        /// return result;
        /// protected IEnumerable<T> GetList<T>(object predicate, IList<ISort> sort = null, 
        ///   IDbTransaction transaction = null, int? commandTimeout = null, bool buffered = false) where T : class {
        ///   var result = connection.GetList<T>(predicate, sort, transaction, commandTimeout, buffered);
        ///   return result;
        /// }
        /// </summary>
        /// <param name="predicate">参数： var predicate = Predicates.Field<Person>(f => f.Active, Operator.Eq, true);</param>
        /// <param name="sort"></param>
        /// <param name="transaction"></param>
        /// <param name="commandTimeout"></param>
        /// <param name="buffered"></param>
        /// <returns></returns>
        public IEnumerable<T> GetList(object predicate, IList<ISort> sort = null, int? commandTimeout = null, bool buffered = false)
        {
            IList<T> result = null;

            using (var connection = GetOpenConnection())
            {
                result = connection.GetList<T>(predicate, sort, null, commandTimeout, buffered).ToList();
            }

            return result;
        }

        /// <summary>
        /// List<ISort> sortList = new List<ISort>();
        /// sortList.Add(Predicates.Sort<myPoco>(x => x.Name, false));
        /// var result = GetList<myPoco>(null, sortList).ToList();
        /// return result;
        /// protected IEnumerable<T> GetList<T>(object predicate, IList<ISort> sort = null, 
        ///   IDbTransaction transaction = null, int? commandTimeout = null, bool buffered = false) where T : class {
        ///   var result = connection.GetList<T>(predicate, sort, transaction, commandTimeout, buffered);
        ///   return result;
        /// }
        /// 参数： var predicate = Predicates.Field<Person>(f => f.Active, Operator.Eq, true);
        /// </summary>
        /// <param name="predicate"></param>
        /// <param name="sort"></param>
        /// <param name="pageIndex">注意：第一页从0开始</param>
        /// <param name="pageSize"></param>
        /// <param name="transaction"></param>
        /// <param name="commandTimeout"></param>
        /// <param name="buffered"></param>
        /// <returns></returns>
        public PagedResultOutput<T> GetPage(object predicate, IList<ISort> sort = null, int pageIndex = 1, int pageSize = 20, int? commandTimeout = null, bool buffered = false)
        {
            IList<T> result = null;
            int total = 0;

            using (var connection = GetOpenConnection())
            {
                total = connection.Count<T>(predicate, commandTimeout: commandTimeout);
                result = connection.GetPage<T>(predicate, sort, pageIndex, pageSize, null, commandTimeout, buffered).ToList();
            }

            return new PagedResultOutput<T> { total = total, rows = result };
        }

        /// <summary>
        /// string sql = "userinfo_insert";  //或者 insert into userinfo (name, phone) values(@login_name, @phone);
        /// using (var conn = GetConnectionFactory()) {
        ///   var rowCounts = conn.Execute(sql,
        ///   new { login_name = "tom", phone = "13800000000" },
        ///   commandType: CommandType.StoredProcedure);  // CommandType.Text
        ///   }
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="param"></param>
        /// <param name="commandType"></param>
        /// <param name="transaction"></param>
        /// <param name="commandTimeout"></param>
        /// <returns></returns>
        public int Execute(string sql, object param = null, CommandType commandType = CommandType.Text, int? commandTimeout = null)
        {
            using (var connection = GetOpenConnection())
            {
                return connection.Execute(sql, param, null, commandTimeout, commandType);
            }
        }

        public long GetSequence(string seqName)
        {
            using (var connection = GetOpenConnection())
            {
                List<long> list = connection.Query<long>(string.Format("select {0}.nextval from dual", seqName)).ToList();
                if (list != null && list.Count >= 1)
                    return list[0];
                else
                    return 0;
            }
        }

        public T Get(long id)
        {
            using (var connection = GetOpenConnection())
            {
                return connection.Get<T>(id);
            }
        }

        public IList<T> GetAll()
        {
            IList<T> result = null;

            using (var connection = GetOpenConnection())
            {
                result = connection.GetList<T>().ToList();
            }

            return result;
        }

        public void Insert(T obj)
        {
            using (var connection = GetOpenConnection())
            {
                connection.Insert(obj);
            }
        }

        public void Update(T obj)
        {
            using (var connection = GetOpenConnection())
            {
                connection.Update(obj);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="param">An anonymous object with key=value types</param>
        public void UpdateFields(object param)
        {
            using (var connection = GetOpenConnection())
            {
                connection.UpdateFields<T>(param);
            }
        }

        public void Delete(T obj)
        {
            using (var connection = GetOpenConnection())
            {
                connection.Delete<T>(obj);
            }
        }
    }
}